package redislock

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/google/uuid"
)

// 常量定义
const (
	DefaultRetryMin = 50 * time.Millisecond  // 默认最小重试间隔
	DefaultRetryMax = 500 * time.Millisecond // 默认最大重试间隔
	MaxRenewRetries = 3                      // 续约失败最大重试次数
)

// Lock 定义Redis分布式锁结构体
type Lock struct {
	key           string
	value         string
	ttl           time.Duration
	waitTime      time.Duration // 锁获取的等待时间，没设置则表示不等待
	retryMin      time.Duration
	retryMax      time.Duration
	getTime       time.Time          // 锁获取时的时间
	ctx           context.Context    // 外部传入的上下文（用于自动释放）
	cancel        context.CancelFunc // 终止续约和自动释放
	renewCh       chan struct{}
	isHeld        bool
	maxHoldTime   time.Duration // 最大持有时间，用于续约（防止无限续期，默认为5*ttl）
	renewRetryCnt int32         // 续约重试计数
	finalizerDone bool          // 标记Finalizer是否已执行
}

// LockOption 定义可选参数函数类型（用于自定义锁参数）
type LockOption func(*Lock)

// WithContext 绑定外部上下文，上下文结束时自动释放锁
func WithContext(ctx context.Context) LockOption {
	return func(l *Lock) {
		l.ctx = ctx
	}
}

// WithWaitTime 设置锁获取的等待时间，没设置则表示不等待
func WithWaitTime(waitTime time.Duration) LockOption {
	return func(l *Lock) {
		if waitTime > 0 {
			l.waitTime = waitTime
		}
	}
}

// WithRetryInterval 设置重试间隔范围（退避策略）
func WithRetryInterval(min, max time.Duration) LockOption {
	return func(l *Lock) {
		if min > 0 {
			l.retryMin = min
		}
		if max > min {
			l.retryMax = max
		}
	}
}

// WithMaxHoldTime 设置最大持有时间（必须大于ttl）
func WithMaxHoldTime(maxHoldTime time.Duration) LockOption {
	return func(l *Lock) {
		if maxHoldTime <= l.ttl {
			l.maxHoldTime = l.ttl * 5 // 确保最大持有时间大于ttl
		}
	}
}

// NewLock 创建一个新的Redis分布式锁实例
// key：锁键名；ttl：锁过期时间；options：可选参数
func NewLock(key string, ttl time.Duration, options ...LockOption) *Lock {
	l := &Lock{
		key:      fullKey(key),
		value:    uuid.New().String(), // 唯一值，用于安全释放
		ttl:      ttl,
		retryMin: DefaultRetryMin,
		retryMax: DefaultRetryMax,
		renewCh:  make(chan struct{}),
	}

	// 应用自定义参数
	for _, opt := range options {
		opt(l)
	}

	// 参数校验
	if l.ttl <= 0 {
		l.ttl = 5 * time.Second // 最小过期时间兜底
	}
	if l.retryMin > l.retryMax {
		l.retryMin, l.retryMax = DefaultRetryMin, DefaultRetryMax
	}
	if l.maxHoldTime <= l.ttl {
		l.maxHoldTime = l.ttl * 5 // 确保最大持有时间大于ttl
	}

	// 设置Finalizer：当锁对象被GC时强制释放（最后防线）
	// 对象被垃圾回收（GC）之前执行特定的清理函数
	runtime.SetFinalizer(l, func(lock *Lock) {
		//fmt.Printf("Finalizer：%v|%v|%v|%v\r\n", lock.key, lock.value, lock.isHeld, lock.finalizerDone)
		if lock.isHeld && !lock.finalizerDone {
			lock.finalizerDone = true
			_, err := lock.Release()
			if err != nil {
				globalLogger.Errorf("lock [%s] finalizer release failed: %v", lock.key, err)
			} else {
				globalLogger.Warnf("lock [%s] released by finalizer (forgot Release()?)", lock.key)
			}
		}
	})

	return l
}

// Acquire 封装获取锁并返回带defer释放的函数（推荐使用方式）
// 示例：defer lock.Acquire()()
func (l *Lock) Acquire() (func(), error) {
	acquired, err := l.Get()
	if err != nil || !acquired {
		return nil, fmt.Errorf("acquire lock failed: %v", err)
	}
	// 返回释放函数，提醒用户用defer调用
	return func() {
		if _, err := l.Release(); err != nil {
			globalLogger.Errorf("lock [%s] release failed in defer: %v", l.key, err)
		}
	}, nil
}

// Get 获取锁，新增上下文监听（自动释放）
func (l *Lock) Get() (bool, error) {
	if l.isHeld {
		return false, errors.New("lock already held")
	}

	// 检查全局Redis客户端是否已初始化
	if redisDo == nil {
		return false, errors.New("redisDoFunc not initialized")
	}

	// 生成内部上下文，结合外部上下文和最大持有时间
	var ctx context.Context
	var cancel context.CancelFunc
	if l.ctx != nil {
		// 外部上下文+最大持有时间取早到者
		ctx, cancel = context.WithDeadline(l.ctx, time.Now().Add(l.maxHoldTime))
	} else {
		// 无外部上下文时，用最大持有时间作为超时
		ctx, cancel = context.WithTimeout(context.Background(), l.maxHoldTime)
	}
	l.ctx = ctx
	l.cancel = cancel

	// 非等待模式
	if l.waitTime <= 0 {
		return l.tryLock()
	}

	// 等待模式（带上下文超时检查）
	beginTime := time.Now()
	randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
	for {
		select {
		case <-ctx.Done():
			return false, fmt.Errorf("lock [%s] context done: %v", l.key, ctx.Err())
		default:
		}

		if time.Since(beginTime) >= l.waitTime {
			return false, fmt.Errorf("lock [%s] wait timeout", l.key)
		}

		acquired, err := l.tryLock()
		if err != nil {
			return false, err
		}
		if acquired {
			return true, nil
		}

		retryInterval := l.retryMin + time.Duration(randGen.Int63n(int64(l.retryMax-l.retryMin)))
		time.Sleep(retryInterval)
	}
}

// tryLock 底层获取锁逻辑（原子操作）
func (l *Lock) tryLock() (bool, error) {
	// 使用Do方法执行SET命令，参数为：NX（不存在则设置，原子操作）、PX（毫秒级过期时间）
	result, err := redisDo(l.ctx, "SET", l.key, l.value, "NX", "PX", l.ttl.Milliseconds())
	if err != nil {
		return false, fmt.Errorf("SET NX failed: %v", err)
	}
	// 获取成功此处会返回OK
	if result == nil {
		return false, nil // 锁已被持有
	}

	// 获取锁成功：初始化上下文并启动续约协程
	l.getTime = time.Now()
	l.isHeld = true
	go l.renewLoop() // 启动续约
	//fmt.Printf("lock get：%v|%v|%v|%v\r\n", l.key,l.value, l.isHeld, l.finalizerDone)
	return true, nil
}

// renewLoop 自动续约循环（安全续约+异常退出）
func (l *Lock) renewLoop() {
	defer close(l.renewCh)

	// 续约间隔 = ttl/3（比ttl/2更保守，减少过期风险）
	renewInterval := l.ttl / 3
	if renewInterval < 100*time.Millisecond {
		renewInterval = 100 * time.Millisecond // 最小续约间隔兜底
	}
	ticker := time.NewTicker(renewInterval)
	defer ticker.Stop()

	for {
		select {
		case <-l.ctx.Done():
			// 上下文终止（外部取消或超时），自动释放锁
			if l.isHeld {
				_, err := l.Release()
				if err != nil {
					globalLogger.Errorf("lock [%s] auto release on context done: %v", l.key, err)
				} else {
					globalLogger.Infof("lock [%s] auto released on context done", l.key)
				}
			}
			return
		case <-ticker.C:
			// 检查是否超过最大持有时间
			if time.Since(l.getTime) >= l.maxHoldTime {
				msg := fmt.Sprintf("lock [%s] exceeds max hold time (%v), auto release", l.key, l.maxHoldTime)
				if _, err := l.Release(); err != nil {
					globalLogger.Errorf("%s: %v", msg, err)
				} else {
					globalLogger.Warnf(msg)
				}
				return
			}

			// 带重试的续约
			renewed, err := l.renewWithRetry()
			if err != nil || !renewed {
				msg := fmt.Sprintf("lock [%s] renew failed", l.key)
				if err != nil {
					msg = fmt.Sprintf("%s: %v", msg, err)
				}
				if _, err := l.Release(); err != nil {
					globalLogger.Errorf("%s: %v", msg, err)
				} else {
					globalLogger.Warnf(msg)
				}
				return
			}
			//fmt.Printf("renew：%v|%v\r\n", l.key,l.value)
			atomic.StoreInt32(&l.renewRetryCnt, 0)
		}
	}
}

// renewWithRetry 续约带重试逻辑
func (l *Lock) renewWithRetry() (bool, error) {
	retryCnt := atomic.LoadInt32(&l.renewRetryCnt)
	for retryCnt < MaxRenewRetries {
		renewed, err := l.renewOnce()
		if err == nil {
			return renewed, nil
		}
		// 短暂延迟后重试（指数退避）
		retryDelay := time.Duration(retryCnt+1) * 100 * time.Millisecond
		time.Sleep(retryDelay)
		retryCnt = atomic.AddInt32(&l.renewRetryCnt, 1)
		globalLogger.Warnf("lock [%s] renew retry %d/%d, error: %v", l.key, retryCnt, MaxRenewRetries, err)
	}
	return false, errors.New("max renew retries exceeded")
}

// renewOnce 执行一次续约（返回是否续约成功）
func (l *Lock) renewOnce() (bool, error) {
	// Lua脚本：校验锁归属+续期（单位：毫秒，避免秒级截断问题）
	script := `
		if redis.call("GET", KEYS[1]) == ARGV[1] then
			return redis.call("PEXPIRE", KEYS[1], ARGV[2])
		else
			return 0
		end
	`
	// 使用Do方法执行EVAL命令，传入毫秒级过期时间（解决ttl含小数秒的问题）
	result, err := redisDo(l.ctx, "EVAL", script, len([]string{l.key}), l.key, l.value, l.ttl.Milliseconds())
	if err != nil {
		return false, err
	}
	// 解析结果（1=成功，0=失败）
	renewed, ok := result.(int64)
	return ok && renewed == 1, nil
}

// Release 安全释放锁（仅释放当前持有且归属自己的锁）
func (l *Lock) Release() (bool, error) {
	// 防御性检查：未持有锁或已释放
	if !l.isHeld {
		return false, errors.New("lock not held")
	}
	defer func() {
		// 无论释放成功与否，标记为未持有并终止续约
		l.isHeld = false
		if l.cancel != nil {
			l.cancel()
			l.cancel = nil // 避免重复调用
		}
		l.finalizerDone = true // 标记finalizer无需再执行
		// 等待续约协程退出（最多等1个续约间隔，避免阻塞）
		select {
		case <-l.renewCh:
		case <-time.After(l.ttl / 3):
			globalLogger.Warnf("lock [%s] renew goroutine exit timeout", l.key)
		}
	}()

	// 安全释放锁的Lua脚本（原子校验+删除）
	script := `
		if redis.call("GET", KEYS[1]) == ARGV[1] then
			return redis.call("DEL", KEYS[1])
		else
			return 0
		end
	`
	// 使用上下文（确保不为nil）
	ctx := l.ctx
	if ctx == nil {
		ctx = context.Background()
	}
	// 使用Do方法执行EVAL命令
	result, err := redisDo(ctx, "EVAL", script, len([]string{l.key}), l.key, l.value)
	if err != nil {
		return false, fmt.Errorf("release lock [%s] failed: %v", l.key, err)
	}
	// 解析结果（1=释放成功，0=锁已被他人持有）
	released, ok := result.(int64)
	if !ok || released != 1 {
		return false, fmt.Errorf("lock [%s] not released (may be held by others)", l.key)
	}
	return true, nil
}

// Clear 强制清理锁（用于异常场景，谨慎使用）
func (l *Lock) Clear() error {
	// 终止续约（若存在）
	if l.cancel != nil {
		l.cancel()
		l.cancel = nil
	}
	l.isHeld = false
	l.finalizerDone = true

	// 强制删除（不校验归属）
	ctx := context.Background()
	_, err := redisDo(ctx, "DEL", l.key)
	if err != nil {
		return fmt.Errorf("clear lock [%s] failed: %v", l.key, err)
	}
	return nil
}

// IsHeld 检查当前锁是否被持有
func (l *Lock) IsHeld() bool {
	return l.isHeld
}

// GetKey 获取锁的完整键名
func (l *Lock) GetKey() string {
	return l.key
}

// GetValue 获取锁的唯一值
func (l *Lock) GetValue() string {
	return l.value
}

// GetTTL 获取锁的过期时间
func (l *Lock) GetTTL() time.Duration {
	return l.ttl
}
